\documentclass[oneside]{ctexbook}%ctexart
 
\usepackage{graphicx}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{fontspec} 
\usepackage{listings}
\lstset{language=C++}
\lstset{breaklines}

\usepackage{geometry}
\geometry{a4paper,scale=0.8}
 
\usepackage{color}
\usepackage{todonotes}
\title{ELF文件}
\author{武峰}
\date{\today}
 
%\setmainfont{Times New Roman}
%\setCJKmainfont{KaiTi}

\begin{document}
\maketitle
\tableofcontents

\chapter{ELF结构}


\chapter {静态链接}

\chapter {动态链接}
动态链接的实现过程中，最主要解决的问题就是代码段与数据段的访问。对于数据段，其在每一个进程中都有一个独立副本。所以对于每一个对动态链接库的数据段的引用都可以使用装载时重定位的方式来解决问题。但是对于代码和数据怎么处理呢？
\section{动态链接的基本原理}
动态链接时有一个最基本的问题需要解决：\textcolor{blue}{我怎么知道这个符号是一个动态链接符号而不是一个静态链接符号呢？}。想要解决这个问题，链接的时候就必须知道目标符号的性质。这要求生成可执行文件的时候要链接动态库。通过链接动态库就可以知道此符号的特性了。

\subsection{动态链接地址的确定}
假设没有动态链接，那么链接完成后所有的地址都确定了，没有再调整的必要。但是动态库是在运行时进行链接的也就是说动态库不知道自己所需符号的地址。但是这个地址有时是必须要知道的。如果是定义在自己内部的符号还好说，可以通过间接寻址找到。但是如何确定外部符号的地址呢？

\begin{figure}[h]
	\centering
	\includegraphics[width=5.5cm,height=5cm]{so1.eps}
	\caption{动态链接地址不确定性}\label{dizhibuqueding}
\end{figure}

解决这个问题的思路比较简单：模块间的数据访问直接通过访问一个变量地址表就可以了。在ELF文件中，这个表叫做GOT的全局偏移表。顺便说一个：用这种方式也可以生成可执行文件，PIE可执行文件。

\subsection{GOT的细节问题}
当一个模块定义了一个全局变量，而编译器却无法确定声明这个变量的其他程序中这个变量是否是一个共享对象定义的变量。于是只能填写绝对地址。连接器知道这个符号其实定义在了其他共享模块之中。但是编译器已经将其填写成不用动态定位的符号。于是连接器只能在可执行文件的.bss段开辟空间。但是共享对象内部对这个符号已经有了定义，这就造成了问题。解决方案是共享模块认为自己模块定义的全局变量其实是其他模块定义的，所以通过GOT可以访问。同时一旦意识到可执行文件中有了这个符号，那么理解将GOT中的指针指向可执行文件中的符号。

另外一个问题：当进程A改变了共享对象中全局变量的值的时候，进程B能感受到全局变量变化吗？当然不应该感觉到，所以变量在不同进程中有不同的副本。这意味着如果模块的数据段也存在访问地址的问题，由于每一个可执行文件中都有模块数据段的独立副本，所以可以知道这个时候对数据段使用装载重定位即可。

\subsection{小结}
fpic的意思还是很明确的，但是需要注意，生成可执行文件的时候需要指定动态库的搜索路径。否则运行时装载找不到你的动态库。\textcolor{red}{使用-Wl -rpath}来指定运行时动态库搜索路径。

\section{延迟绑定}
动态链接在装载后运行前进行。但是假设动态链接的符号很多，那么动态链接会非常耗时间。那么可以将动态库内部\textcolor{blue}{函数}的链接推迟到其第一次运行时。通过插入一小段特殊的初始化代码就可以实现延迟绑定的效果。但是这个并不是通过GOT表直接实现的间接跳转，而是通过PLT与GOT相互配合达到结果的。

\section{动态链接的段}
有一些段比较重要。
\begin{enumerate}
	\item .dynamic 保存了共享对象一些重要的要素。例如动态链接符号表的位置以及动态链接共享对象的位置。DT\_RPATH指定了运行的时候去哪里找动态链接库文件而-L则制定了其依赖的库，这样就可以知道符号属性（(否则无法确定是什么符号)但是运行时的库还是药用rpath来指定。
	 
	 \item .symtab 就是保存了文件的导入导出符号。这些符号同时也保存在.symtab这个大符号表里面。	
\end{enumerate}
\section{运行时主动链接}

当程序运行时主动发动动态链接的时候，就成了“插件化”程序设计的基础。分为四个部分打开链接库、关闭链接库、查找符号、错误处理。动态库的头文件为\textcolor{blue}{<dlfcn.h>}，链接库的地址是/lib/libdl.so.2。


\subsection{dlopen}
函数dlopen负责打开动态库。
\begin{lstlisting}
#include <dlfcn.h>
void * dlopen(char *file_path,int flag);
\end{lstlisting}

file\_path是绝对路径时，直接打开指定的动态库。如果是相对路径，那么按照如下规则寻找。首先LD\_LIBRARAY\_PATH环境变量指定的动态链接库。而后/etc/ld.so.cache指定了动态库的路径。最后查找/lib和/user/lib这些目录。

flag是定绑定方式，RTLD\_LAZY，延迟绑定直到第一次运行时才开始绑定。RTLD\_NOW，直接绑定。

\textcolor{blue}{dlopen函数返回的是一个打开模块的句柄，后面的dlsym和dlclose使用。}

\subsection{dlsym}
dlsym查找符号，是模块化的基础。
\begin{lstlisting}
#include <dlfcn.h>
void * dlopen(void *handle,char *symbol);
\end{lstlisting}
handle是之前open时的句柄，symbol是符号名。如果是函数，那么返回函数地址。如果是变量，返回变量地址。如果是常量，那么返回常量值。出错返回NULL，如果返回的常量正好是NULL，那么需要下一个函数来帮忙确定是否是错误。

\section {国内外研究现状}
\subsection{国内研究现状}
\subsection{国外研究现状}
 
%\includegraphics[width=0.6\textwidth]{shot}
\end{document}
